// 市区范围的旋转底座

import {
	Cartesian3,
	CircleGeometry,
	EllipsoidSurfaceAppearance,
	GeometryInstance,
	Material,
	GroundPrimitive,
	Primitive,
	PrimitiveCollection,
	Viewer,
	Color
} from 'cesium'

import {
	point as tfPoint,
	points as tfPoints,
	convex as tfConvex,
	center as tfCenter,
	distance as tfDistance
} from '@turf/turf'

class Baseplate {

	viewer : Viewer | null
	isLoad: boolean = false
	isGrowing : boolean = false
	
	baseplatePrimitives : PrimitiveCollection | null
	rotateColor: Array<any> = [102,184,217, 0.6]
	fixedColor: Array<any> = [102,184,217, 0.2]
	diffuseColor: Array<any> = [102,184,217, 0.2]

	constructor(viewer : Viewer) {
		this.viewer = viewer
	}

	// 加载数据
	async load() {
		if (this.isLoad == true) return
		this.isLoad = true

		this.baseplatePrimitives = new PrimitiveCollection()
		this.viewer.scene.primitives.add(this.baseplatePrimitives)

		const result = await import(`../../../../static/gisData/China.json`)

		const lonlats = this.parseGeojson(result)

		const points = tfPoints(lonlats)
		const convex = tfConvex(points)
		const center = tfCenter(convex)
		const distance = tfDistance(center.geometry, convex.geometry.coordinates[0][0], { units: `meters` })

		this.createFixedPlatePrimitive(this.viewer, center.geometry.coordinates, distance)
		this.createRotatePlatePrimitive(this.viewer, center.geometry.coordinates, distance)
		this.createDiffusionPrimitive(this.viewer, center.geometry.coordinates, distance)
	}

	// 将geojson解析成坐标数组
	parseGeojson(geojson : any) {
		if (geojson == null) return []
		if (geojson.type == 'FeatureCollection') {
			return parseFeatureC(geojson)
		}
		else if (geojson.type == 'Feature') {
			return parseFeacture(geojson)
		}

		function parseFeatureC(geojson : any) {
			if (geojson.type == 'FeatureCollection') {
				return geojson.features.map((item : any) => { return parseFeacture(item) })[0]
			}
			else return null
		}

		function parseFeacture(geojson : any) {
			if (geojson.type != 'Feature') return null
			if (geojson.geometry == null) return null
			const geometry = geojson.geometry
			return parseGeometry(geometry)
		}

		function parseGeometry(geometry : any) {
			if (geometry.type == 'MultiPolygon') {
				const coordinates = geometry.coordinates[0][0]
				let lonlats = []
				coordinates.forEach((item) => { lonlats.push([...item]) })
				return lonlats
			}
			else if (geometry.type == 'Polygon') {
				const coordinates = geometry.coordinates[0]
				let lonlats = []
				coordinates.forEach((item) => { lonlats.push([...item]) })
				return lonlats
			}
			else return null
		}
	}

	// 创建固定底盘
	private createFixedPlatePrimitive(viewer : Viewer, center : Array<number>, radius : number) {
        const color = this.fixedColor
		const circleGeom = new CircleGeometry({
			center: Cartesian3.fromDegrees(center[0], center[1]),
			radius: radius
		})
		const instance = new GeometryInstance({
			geometry: circleGeom
		})
		const appearance = new EllipsoidSurfaceAppearance({
			material: new Material({
				fabric: {
					uniforms: {
						time: 0,
						color: new Color(color[0]/255,color[1]/255,color[2]/255,color[3]),
						grow: 1
					},
					source: `
					    uniform vec4 color;
						uniform float grow;
					    float scrollH = 0.4;
						float PI = 3.1415926;
					    
					    czm_material czm_getMaterial(czm_materialInput materialInput) {
					    	czm_material material = czm_getDefaultMaterial(materialInput);
							vec2 st = materialInput.st;
							//float scale =  1.0 / grow;
							//if(scale > 1000.0) scale = 1000.0;
							float scale = 1.0;
							st = st * scale;
					    	
							float dis = sqrt(pow(st.s - scale / 2.0, 2.0) + pow(st.t - scale / 2.0, 2.0));
							material.alpha = 0.0;
							if(dis>0.395 && dis<0.4) {
								material.diffuse = color.rgb;
								material.alpha = color.a;
								
								float angle = acos(dot(vec2(0.0,0.5),vec2(st.s-0.5,st.t-0.5))/(0.5 * dis));
								vec3 rotate = cross(vec3(0.0,0.5,0.0),vec3(st.s-0.5,st.t-0.5,0.0));
								if(rotate.z > 0.0) angle = 2.0 * PI - angle;
								angle += 0.7;
							
							    angle -= time;
								float diff = mod(angle, (2.0 * PI / 3.0));
								if(diff < 2.0 * PI / 3.0 / 6.0 || diff > 2.0 * PI / 3.0 / 6.0 * 5.0) {
									material.alpha = 0.05;
								}
							}
							
							material.alpha *= grow;
							
					    	return material;
					    }
					`
				}
			})
		})
		const primitive = new GroundPrimitive({
			geometryInstances: instance,
			appearance,
			asynchronous: true,
			releaseGeometryInstances: true,
			compressVertices: true,
			allowPicking: false
		})
		this.baseplatePrimitives.add(primitive)
		this.viewer.scene.preRender.addEventListener(this._renderFixedEvent.bind(appearance.material))
	}
	private _renderFixedEvent() {
		const intervalTime = 2 * 3.1415926 * 20;
		const newTime = Date.now() / 1000;
		const interval = newTime % intervalTime / 30;
		
		this.uniforms.time = interval
	}
	

	// 创建旋转底盘
	private createRotatePlatePrimitive(viewer : Viewer, center : Array<number>, radius : number) {
		const color = this.rotateColor
		const circleGeom = new CircleGeometry({
			center: Cartesian3.fromDegrees(center[0], center[1]),
			radius: radius*(1/2)
		})
		const instance = new GeometryInstance({
			geometry: circleGeom,
		})
		const appearance = new EllipsoidSurfaceAppearance({
			material: new Material({
				fabric: {
					uniforms: {
						color: new Color(color[0]/255,color[1]/255,color[2]/255,color[3]),
						time: 0,
						grow: 1
					},
					source: `
					    uniform float time;
					    uniform vec4 color;
						uniform float grow;
					    float scrollH = 0.4;
						float PI = 3.1415926;
					    
					    czm_material czm_getMaterial(czm_materialInput materialInput) {
					    	czm_material material = czm_getDefaultMaterial(materialInput);
					    	vec2 st = materialInput.st;
							//float scale =  1.0 / grow;
							//if(scale > 1000.0) scale = 1000.0;
							float scale = 1.0;
					    	
							float dis = sqrt(pow(st.s - scale / 2.0, 2.0) + pow(st.t - scale / 2.0, 2.0));
							material.alpha = 0.0;
							if(dis>0.49 && dis<0.5) {
								material.diffuse = color.rgb;
								material.alpha = color.a;
								
								float angle = acos(dot(vec2(0.0,scale / 2.0),vec2(st.s-scale / 2.0,st.t-scale / 2.0))/(scale / 2.0 * dis));
								vec3 rotate = cross(vec3(0.0,scale / 2.0,0.0),vec3(st.s-scale / 2.0,st.t-scale / 2.0,0.0));
								if(rotate.z > 0.0) angle = 2.0 * PI - angle;
								angle -= time;
								float diff = mod(angle, (2.0 * PI / 5.0));
								if(diff < 2.0 * PI / 5.0 / 6.0 || diff > 2.0 * PI / 5.0 / 6.0 * 5.0) {
									material.alpha = 0.0;
								}
							}
							
							if(dis > 0.28 + 0.05 && dis < 0.3 + 0.05) {
								material.diffuse = color.rgb;
								material.alpha = color.a;
								
								float angle = acos(dot(vec2(0.0,scale / 2.0),vec2(st.s-scale / 2.0,st.t-scale / 2.0))/(scale / 2.0 * dis));
								vec3 rotate = cross(vec3(0.0,scale / 2.0,0.0),vec3(st.s-scale / 2.0,st.t-scale / 2.0,0.0));
								if(rotate.z > 0.0) angle = 2.0 * PI - angle;
								angle += time;
								float diff = mod(angle, (2.0 * PI / 5.0));
								if(diff < 2.0 * PI / 5.0 / 6.0 || diff > 2.0 * PI / 5.0 / 6.0 * 5.0) {
									material.alpha = 0.0;
								}
							}
							
							material.alpha *= grow;
					    
					    	return material;
					    }
					`
				}
			})
		})
		const primitive = new GroundPrimitive({
			geometryInstances: instance,
			appearance,
			asynchronous: true,
			releaseGeometryInstances: true,
			compressVertices: true,
			allowPicking: false
		})
		this.baseplatePrimitives.add(primitive)
		viewer.scene.preRender.addEventListener(this._renderRotateEvent.bind(appearance.material))
	}
	
	private _renderRotateEvent() {
		const intervalTime = 2 * 3.1415926;
		const newTime = Date.now() / 1000;
		const interval = newTime % intervalTime / 5;
		
		this.uniforms.time = interval
	}

	// 创建放射底盘
	private createDiffusionPrimitive(viewer : Viewer, center : Array<number>, radius : number) {
        const color = this.diffuseColor
		const circleGeom = new CircleGeometry({
			center: Cartesian3.fromDegrees(center[0], center[1]),
			radius: radius * 20
		})
		const instance = new GeometryInstance({
			geometry: circleGeom
		})
		const appearance = new EllipsoidSurfaceAppearance({
			material: new Material({
				fabric: {
					uniforms: {
						time: 0,
						color: new Color(color[0]/255,color[1]/255,color[2]/255,color[3])
					},
					source: `
					    uniform float time;
					    uniform vec4 color;
					    float scrollH = 0.4;
						float PI = 3.1415926;
						float width = 0.08;
					    
					    czm_material czm_getMaterial(czm_materialInput materialInput) {
					    	czm_material material = czm_getDefaultMaterial(materialInput);
					    	vec2 st = materialInput.st;
					    	
							float dis = sqrt(pow(st.s-0.5, 2.0) + pow(st.t-0.5, 2.0));
							material.diffuse = color.rgb;
							material.alpha = 0.0;
							float fardis = mod(time, 0.5);
							if(dis>fardis - width && dis<fardis && dis>0.01) {
								material.diffuse = color.rgb;
								float alpha = color.a - fardis/0.5*color.a;
								alpha = alpha - abs(fardis - dis - (width / 2.0)) / (width / 2.0) * alpha;
								material.alpha = alpha;
							}
							
					    	return material;
					    }
					`
				}
			})
		})
		const primitive = new GroundPrimitive({
			geometryInstances: instance,
			appearance,
			asynchronous: true,
			releaseGeometryInstances: true,
			compressVertices: true,
			allowPicking: false
		})
		this.baseplatePrimitives.add(primitive)
		viewer.scene.preRender.addEventListener(this._renderDiffusionEvent.bind(appearance.material))
	}

	_renderDiffusionEvent() {
		let newTime = Date.now()
		let interval = newTime % 10000 / 10000
	
		this.uniforms.time = interval
	}
	

	// 卸载数据
	unload() {
		if (this.isLoad == false) return
		this.isLoad = false
		this.viewer.scene.primitives.remove(this.baseplatePrimitives)
		//this.baseplatePrimitives.destroy()
		this.baseplatePrimitives = null
		this.viewer.scene.preRender.removeEventListener(this._renderRotateEvent)
	}
	
	async show() {
		if(this.isGrowing) return
		this.isGrowing = true
		
		this.baseplatePrimitives.show = true
		await this.countdownFrameRender(1, (rate : number) => {
			for(let i = 0;i< this.baseplatePrimitives.length; i++){
				const primitive = this.baseplatePrimitives.get(i);
				primitive.appearance.material.uniforms.grow = rate;
			}
		})
		
		this.isGrowing = false
	}
	
	async hide() {
		if(this.isGrowing) return
		this.isGrowing = true

		await this.countdownFrameRender(1, (rate : number) => {
			for(let i = 0;i< this.baseplatePrimitives.length; i++){
				const primitive = this.baseplatePrimitives.get(i);
				primitive.appearance.material.uniforms.grow = 1 - rate;
			}
		})
		
		this.baseplatePrimitives.show = false
		
		this.isGrowing = false
	}
	
	/**
	 * 倒计时帧动画
	 * @param {number} time 倒计时时间
	 * @param {Function} callback 渲染帧回调
	 */
	private countdownFrameRender(time : number, callback : CountdownFrameCallback) {
		const viewer = this.viewer
		return new Promise((resolve) => {
			const originalDate = Date.now()
			const frameFunc = () => {
				const dateNow = Date.now()
				const spend = dateNow - originalDate
				if (spend > time * 1000) {
					viewer.scene.preRender.removeEventListener(frameFunc)
					resolve('')
				}
				let rate = spend / (time * 1000)
				if (rate > 1) rate = 1
				callback(rate)
			}
			viewer.scene.preRender.addEventListener(frameFunc)
		})
	}
}

export default Baseplate

/**
 * 倒计时帧回调
 */
type CountdownFrameCallback = (rate : number) => void